package com.trustbook.visitor.service;

import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;

import com.trustbook.visitor.log.LogManager;

/**
 * 
 * This is base class which will be extended by actual implementation classes.
 * It handles the communication with server and handles all the service and
 * connection time out. It also provides the mechanism to marshal and un-marshal
 * the Java objects.
 * 
 * @author Rahul_Mittal@dell.com
 * 
 */
public class XMLServiceInterceptor {

	private static final String TAG = "FSM.XMLServiceInterceptor";

	private DefaultHttpClient client;

	private Serializer serializer;

	private static final String CONTENT_TYPE = "text/plain; charset=utf-8";
	// Time in milliseconds for service operation time out.
	private static final int SERVICE_OPERATION_TIME_OUT = 60000 * 2;
	// Time in milliseconds for connection time out.
	private static final int CONNECTION_TIME_OUT = 15000;

	/**
	 * Initialize the connection time out parameters. Initialize the serialize
	 * and http client.
	 */
	public XMLServiceInterceptor() {
		client = new DefaultHttpClient();
		HttpParams httpParameters = client.getParams();
		// Set the timeout in milliseconds until a connection is established.
		HttpConnectionParams.setConnectionTimeout(httpParameters, CONNECTION_TIME_OUT);
		// Set the timeout for service operation completion. wait for 2 minutes
		// before timing out.
		HttpConnectionParams.setSoTimeout(httpParameters, SERVICE_OPERATION_TIME_OUT);
		serializer = new Persister();
	}

	/**
	 * serialization - Converting java to xml String This will take the Object
	 * with annotations applied and convert the same to XML which will
	 * eventually be passed to the server as request.
	 * 
	 * @param request
	 *            Object that needs to be converted to XML.
	 * @return XML string object
	 */
	public String marshalObjectToXML(Object request) {
		LogManager.debug(TAG, "Mashalling Object : " + request);
		Writer writer = new StringWriter();
		StringBuffer dataBuffer = new StringBuffer("");
		try {
			serializer.write(request, writer);
		} catch (Exception ex) {
			LogManager.error(TAG, "Not able to serialize Java to XML in marshalObject() method.", ex);
			return dataBuffer.toString();
		}
		// write characters to writer.
		dataBuffer = ((StringWriter) writer).getBuffer();
		// LogManager.debug(TAG, "Request XML :\n" + dataBuffer);
		try {
			writer.close();
		} catch (IOException ioEx) {
			LogManager.error(TAG, "IOException while closing stream", ioEx);
		}

		return dataBuffer.toString();
	}

	/**
	 * 
	 * headers.add(new BasicNameValuePair("Username","Android"));
	 * headers.add(new BasicNameValuePair("Token","Andr01d@#1234TEST"));
	 * headers.add(new BasicNameValuePair("Android","2")); for(NameValuePair
	 * h:headers){ post.addHeader(h.getName(), h.getValue()); }
	 * 
	 * 
	 * Post message to server implementation.
	 * 
	 * @param url
	 *            eg.
	 *            "http://207.124.150.208/DellFSMSvc/FSMService.svc/FSM/Credentials"
	 * @param postData
	 *            XML request data according to the service agreement.
	 * @return xml response from the server.
	 */
	public String postToServer(String url, String postData) {
		LogManager.debug(TAG, "Enter postToServer() url : " + url + " postData :\n" + postData);
		HttpPost post = new HttpPost(url);
		// Create a post request to call the login url.
		StringEntity ent = null;
		try {
			ent = new StringEntity(postData, HTTP.UTF_8);
		} catch (UnsupportedEncodingException uEx) {
			LogManager.error(TAG, "UnsupportedEncodingException thrown while posting to server.", uEx);
		}

		ent.setContentType(CONTENT_TYPE);
		post.setEntity(ent);
		post.setHeader("Username", "Android");
		post.setHeader("Token", "Andr01d@#1234TEST");
		post.setHeader("Platform", "2");
		LogManager.debug(TAG, "Request - ContentLength :" + ent.getContentLength());

		HttpResponse getResponse = null;
		try {
			getResponse = client.execute(post);
		} catch (ClientProtocolException cPEx) {
			post.abort();
			LogManager.error(TAG, "ClientProtocolException while posting to server.", cPEx);
			return null;
		} catch (IOException ioEx) {
			post.abort();
			LogManager.error(TAG, "IOException while posting to server.", ioEx);
			return null;
		}
		int statusCode = -1;
		if (getResponse != null) {
			if (getResponse.getStatusLine() != null) {
				statusCode = getResponse.getStatusLine().getStatusCode();
			}
		}
		LogManager.debug(TAG, "Response - StatusCode :" + statusCode + " Response - Reason :" + getResponse.getStatusLine().getReasonPhrase());
		if (statusCode == -1 || statusCode != HttpStatus.SC_OK) {
			LogManager.debug(TAG, "Response Error from server while posting. Status code " + statusCode + " Reason :" + getResponse.getStatusLine().getReasonPhrase());
			return null;
		}
		HttpEntity getResponseEntity = getResponse.getEntity();
		LogManager.debug(TAG, "Response - ContentType :" + getResponseEntity.getContentType().getValue());
		LogManager.debug(TAG, "Response - ContentLength :" + getResponseEntity.getContentLength());
		String response = "";
		if (getResponseEntity != null) {
			try {
				response = EntityUtils.toString(getResponseEntity);
				LogManager.debug(TAG, "Service Response :\n\n" + response + "\n\n");
			} catch (ParseException parseEx) {
				post.abort();
				LogManager.error(TAG, "ParseException thrown while converting the response xml to String.", parseEx);
				return null;
			} catch (IOException ioEx) {
				post.abort();
				LogManager.error(TAG, "IOException thrown while converting the response XML to String.", ioEx);
				return null;
			}
		}
		return response;
	}

	/**
	 * Get data from server implementation.
	 * 
	 * @param url
	 * @return XML service response according to the service agreement.
	 */
	public String getFromServer(String url) {
		LogManager.debug(TAG, "Enter getFromServer() url : " + url);
		HttpGet getData = new HttpGet(url);
		getData.setHeader("Username", "Android");
		getData.setHeader("Token", "Andr01d@#1234TEST");
		getData.setHeader("Platform", "2");

		HttpResponse getResponse = null;
		try {
			getResponse = client.execute(getData);
		} catch (ClientProtocolException cPEx) {
			getData.abort();
			LogManager.error(TAG, "ClientProtocolException while getting response from server.", cPEx);
			return null;
		} catch (IOException ioEx) {
			getData.abort();
			LogManager.error(TAG, "IOException while getting response from server.", ioEx);
			return null;
		}
		int statusCode = -1;
		if (getResponse != null) {
			if (getResponse.getStatusLine() != null) {
				statusCode = getResponse.getStatusLine().getStatusCode();
			}
		}
		LogManager.debug(TAG, "StatusCode :" + statusCode + " Reason :" + getResponse.getStatusLine().getReasonPhrase());
		if (statusCode == -1 || statusCode != HttpStatus.SC_OK) {
			LogManager.debug(TAG, "Response Error from server while getting response. Status code " + statusCode + " Reason :" + getResponse.getStatusLine().getReasonPhrase());
			return null;
		}
		HttpEntity getResponseEntity = getResponse.getEntity();
		LogManager.debug(TAG, "getContentType :" + getResponseEntity.getContentType().getValue());
		LogManager.debug(TAG, "getContentLength :" + getResponseEntity.getContentLength());
		String response = "";
		if (getResponseEntity != null) {
			try {
				response = EntityUtils.toString(getResponseEntity);
				LogManager.debug(TAG, "Service Response :\n\n" + response + "\n\n");
			} catch (ParseException parseEx) {
				getData.abort();
				LogManager.error(TAG, "ParseException thrown while converting the response xml to String.", parseEx);
				return null;
			} catch (IOException ioEx) {
				getData.abort();
				LogManager.error(TAG, "IOException thrown while converting the response XML to String.", ioEx);
				return null;
			}
		}
		return response;
	}

	/**
	 * Get data from server implementation.
	 * 
	 * @param url
	 * @return XML service response according to the service agreement.
	 */
	public synchronized byte[] getDataFromServer(String url) {
		LogManager.debug(TAG, "Enter getFromServer() url : " + url);
		HttpGet getData = new HttpGet(url);
		getData.setHeader("Username", "Android");
		getData.setHeader("Token", "Andr01d@#1234TEST");
		getData.setHeader("Platform", "2");

		HttpResponse getResponse = null;
		try {
			getResponse = client.execute(getData);
		} catch (ClientProtocolException cPEx) {
			getData.abort();
			LogManager.error(TAG, "ClientProtocolException while getting response from server.", cPEx);
			return null;
		} catch (IOException ioEx) {
			getData.abort();
			LogManager.error(TAG, "IOException while getting response from server.", ioEx);
			return null;
		}
		int statusCode = -1;
		if (getResponse != null) {
			if (getResponse.getStatusLine() != null) {
				statusCode = getResponse.getStatusLine().getStatusCode();
			}
		}
		LogManager.debug(TAG, "StatusCode :" + statusCode + " Reason :" + getResponse.getStatusLine().getReasonPhrase());
		if (statusCode == -1 || statusCode != HttpStatus.SC_OK) {
			LogManager.debug(TAG, "Response Error from server while getting response. Status code " + statusCode + " Reason :" + getResponse.getStatusLine().getReasonPhrase());
			return null;
		}
		HttpEntity getResponseEntity = getResponse.getEntity();
		LogManager.debug(TAG, "getContentType :" + getResponseEntity.getContentType().getValue());
		LogManager.debug(TAG, "getContentLength :" + getResponseEntity.getContentLength());
		byte[] response = null;
		if (getResponseEntity != null) {
			try {
				response = EntityUtils.toByteArray(getResponseEntity);
				LogManager.debug(TAG, "Service Response :\n\n" + new String(response) + "\n\n");
			} catch (ParseException parseEx) {
				getData.abort();
				LogManager.error(TAG, "ParseException thrown while converting the response xml to String.", parseEx);
				return null;
			} catch (IOException ioEx) {
				getData.abort();
				LogManager.error(TAG, "IOException thrown while converting the response XML to String.", ioEx);
				return null;
			}
		}
		return response;
	}

	/**
	 * @return the client
	 */
	public DefaultHttpClient getClient() {
		return client;
	}

	/**
	 * @param client
	 *            the client to set
	 */
	public void setClient(DefaultHttpClient client) {
		this.client = client;
	}

	/**
	 * @return the serializer
	 */
	public Serializer getSerializer() {
		return serializer;
	}

	/**
	 * @param serializer
	 *            the serializer to set
	 */
	public void setSerializer(Serializer serializer) {
		this.serializer = serializer;
	}

}
